using System;
using System.IO;
using System.Text;
using WindowsAPI.Service;

namespace WindowsAPI.PE
{
    [CopyRight("http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=63745")]
    public abstract class BaseCoffReader
    {
        private const int PeSignatureOffsetLocation = 0x3c;
        private const int PeSignatureSize = 4;
        private const string PeSignatureContent = "PE";

        private readonly string f_FileName;

        public string Name { get { return GetType().Name; } }

        protected BaseCoffReader(String FileName)
        {
            if(!File.Exists(FileName))
                throw new FileNotFoundException("File does not exists.", FileName);
            f_FileName = FileName;
        }

        public CoffHeader Parse()
        {
            using(var br = new BinaryReader(File.Open(f_FileName, FileMode.Open, FileAccess.Read, FileShare.Read)))
            {
                SeekToPeSignature(br);
                if(!IsValidPeSignature(br))
                    throw new BadImageFormatException();
                return ParseInternal(br);
            }
        }

        private bool IsValidPeSignature(BinaryReader br)
        {
            // read 4 bytes to get the PE signature
            var lv_PeSigBytes = br.ReadBytes(PeSignatureSize);
            // convert it to a string and trim \0 at the end of the content
            var lv_PeContent = Encoding.Default.GetString(lv_PeSigBytes).TrimEnd('\0');
            // check if PE is in the content
            return lv_PeContent.Equals(PeSignatureContent);
        }

        private void SeekToPeSignature(BinaryReader br)
        {
            // seek to the offset for the PE signagure
            br.BaseStream.Seek(PeSignatureOffsetLocation, SeekOrigin.Begin);
            // read the offset
            var lv_OffsetToPeSig = br.ReadInt32();
            // seek to the start of the PE signature
            br.BaseStream.Seek(lv_OffsetToPeSig, SeekOrigin.Begin);
        }

        protected abstract CoffHeader ParseInternal(BinaryReader br);
    }
}